package com.jason.common.file.word;

import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.text.StrPool;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.jason.common.file.word.dto.WordImportQuestionData;
import com.jason.common.file.word.dto.WordPictureInfo;
import com.jason.common.file.word.dto.WordTemplateTree;
import com.jason.common.file.word.extension.WordPictureUploadInterface;
import com.jason.common.file.word.util.ConcurrentStopWatch;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Word导入上下文容器
 * 主要使用模板方法模式，run()方法作为整个Word导入的业务逻辑流程入口
 *
 * @author guozhongcheng
 * @since 2023/9/28
 **/
@Slf4j
@SuppressWarnings("unused")
public abstract class AbstractWordImportQuestionContext {
    /**
     * 并发执行时长监控秒表
     */
    protected final ConcurrentStopWatch STOP_WATCH;
    /**
     * Word文档文件名称+.+文件类型
     */
    protected final String FILE_NAME_AND_TYPE;
    /**
     * 支持的Word文件类型列表
     */
    protected final static List<String> WORD_FILE_TYPE_NAME_LIST = ListUtil.toList("docx");
    /**
     * 支持的Word文档中的图片格式列表
     */
    protected final static List<String> WORD_PICTURE_FORMAT_LIST = ListUtil.toList("png", "jpg");
    /**
     * 模板定义类对象
     */
    protected final Class<?> TEMPLATE_DEFINITION_CLASS;
    /**
     * word文档文件输入流
     */
    protected final InputStream DOCX_FILE_INPUT_STREAM;

    /**
     * 文件服务器资源路径映射集合
     */
    protected final Map<String, String> REMOTE_FILE_MAPPING_MAP = new ConcurrentHashMap<>(16);
    protected final Map<String, String> MAPPING_INFO_MAP = new HashMap<>(16);

    /**
     * word文档中的图片信息映射集合
     */
    protected final Map<String, WordPictureInfo> WORD_PIC_INFO_MAP = new ConcurrentHashMap<>(16);
    /**
     * word导入试题数据列表
     */
    protected final List<WordImportQuestionData> QUESTION_DATA_LIST = new CopyOnWriteArrayList<>();

    /**
     * 执行计划字符串
     */
    private String prettyPrint = "";

    /**
     * 开启发生异常时抛出Exception
     */
    protected boolean throwExceptionStatus = true;
    /**
     * 异常信息
     */
    protected String exceptionMsg = "";
    protected Exception exception;
    /**
     * 是否发生异常
     */
    protected boolean exceptionStatus = false;

    /**
     * 开启文件上传远程服务器映射
     */
    protected boolean enableFileRemoteMapping = false;
    /**
     * 开启并发执行
     */
    protected boolean enableConcurrent = false;
    /**
     * 线程池。
     * 当{@link AbstractWordImportQuestionContext#enableConcurrent} 为true时，需要赋值。
     */
    protected ThreadPoolExecutor threadPoolExecutor;
    /**
     * 临时创建线程池状态
     */
    private boolean newThreadPoolStatus = false;
    /**
     * 异步关闭状态
     */
    private boolean asyncClose = false;

    /**
     * 本地文件保存基础目录。
     * 当{@link AbstractWordImportQuestionContext#enableFileRemoteMapping} 为false时，需要赋值
     */
    protected String localSaveFileBaseDir;

    /**
     * 最小图片宽度，单位:px
     */
    protected int minPicWidth = 150;
    /**
     * 最小图片高度，单位:px
     */
    protected int minPicHeight = 100;
    /**
     * 最大图片宽度，单位:px
     */
    protected int maxPicWidth = 1000;
    /**
     * 最大图片高度，单位:px
     */
    protected int maxPicHeight = 1000;
    /**
     * 固定图片宽度，单位:px
     */
    protected int fixedPicWidth;
    /**
     * 固定图片高度，单位:px
     */
    protected int fixedPicHeight;

    /**
     * word文档中的图片等文件上传服务器逻辑接口
     */
    protected WordPictureUploadInterface wordPictureUploadInterface;

    /**
     * word模板树
     */
    protected WordTemplateTree wordTemplateTree;
    /**
     * word文档文本字符串
     */
    protected String wordTextStr = "";

    protected AbstractWordImportQuestionContext(String fileNameAndType,
                                                Class<?> templateDefinitionClass,
                                                InputStream docxFileInputStream) {
        this.TEMPLATE_DEFINITION_CLASS = templateDefinitionClass;
        this.DOCX_FILE_INPUT_STREAM = docxFileInputStream;
        this.FILE_NAME_AND_TYPE = fileNameAndType;
        this.STOP_WATCH = new ConcurrentStopWatch(fileNameAndType);
    }

    /**
     * 核心主流程方法
     *
     * @return 执行结果
     */
    public List<WordImportQuestionData> execute() throws Throwable {
        try {
            // 前置校验
            this.STOP_WATCH.start("前置校验");
            this.preCheck(this.DOCX_FILE_INPUT_STREAM);
            this.STOP_WATCH.stop("前置校验");

            // 加载前置数据
            this.STOP_WATCH.start("加载前置数据");
            this.loadPrepareData(this.DOCX_FILE_INPUT_STREAM);
            this.STOP_WATCH.stop("加载前置数据");

            // 判断是否开启并发执行
            if (this.enableConcurrent) {
                // 构建模版树对象
                CompletableFuture<WordTemplateTree> cf1 = this.asyncBuildTemplateTree(this.TEMPLATE_DEFINITION_CLASS);
                // 加载Word文档文件文本数据
                CompletableFuture<String> cf2 = this.asyncLoadWordText(this.TEMPLATE_DEFINITION_CLASS);
                // 加载word文档内部文件映射上传远程服务器的资源URL或本地磁盘URI
                CompletableFuture<Map<String, String>> cf3 = this.asyncLoadInnerFileMapping();
                // 并行执行
                CompletableFuture.anyOf(cf1, cf2, cf3).join();
                this.wordTemplateTree = cf1.get();
                this.wordTextStr = cf2.get();
                this.MAPPING_INFO_MAP.putAll(cf3.get());
            } else {
                // 构建模版树对象
                this.STOP_WATCH.start("构建模版树对象");
                this.wordTemplateTree = this.buildTemplateTree(this.TEMPLATE_DEFINITION_CLASS);
                this.STOP_WATCH.stop("构建模版树对象");
                // 加载Word文档文件文本数据
                this.STOP_WATCH.start("加载Word文档文件文本数据");
                this.wordTextStr = this.loadWordText(this.TEMPLATE_DEFINITION_CLASS);
                this.STOP_WATCH.stop("加载Word文档文件文本数据");
                // 加载word文档内部文件映射上传远程服务器的资源URL或本地磁盘URI
                this.STOP_WATCH.start("加载图片、视频等文件映射数据");
                this.MAPPING_INFO_MAP.putAll(this.loadInnerFileMapping());
                this.STOP_WATCH.stop("加载图片、视频等文件映射数据");
            }

            // 前置处理
            this.STOP_WATCH.start("前置处理");
            this.preProcessor();
            this.STOP_WATCH.stop("前置处理");

            // 解析成具体对象
            this.STOP_WATCH.start("解析成具体对象");
            this.parseObj(this.QUESTION_DATA_LIST, this.wordTemplateTree);
            this.STOP_WATCH.stop("解析成具体对象");

            // 后置处理
            this.STOP_WATCH.start("后置处理");
            this.postProcessor(this.QUESTION_DATA_LIST);
            this.STOP_WATCH.stop("后置处理");
        } catch (Exception e) {
            // 异常处理
            this.STOP_WATCH.stopAll();
            this.exceptionStatus = true;
            if (this.throwExceptionStatus) {
                throw e;
            } else {
                this.exception = e;
                if (e instanceof WordImportException) {
                    WordImportException ex1 = (WordImportException) e;
                    this.exceptionMsg = "截取段落->\n" + ex1.getSubErrorParagraph().trim()
                            + "\n" + "出现错误，错误原因->" + ex1.getErrMsg().trim();
                } else {
                    this.exceptionMsg = ExceptionUtil.stacktraceToString(e);
                }
            }
        } finally {
            // 开启异步关闭
            if (this.asyncClose) {
                CompletableFuture.runAsync(() -> {
                    try {
                        this.close(this.DOCX_FILE_INPUT_STREAM);
                    } catch (IOException e) {
                        throw new BizException("异步关闭Word导入资源失败", e);
                    }
                });
                this.STOP_WATCH.stopAll();
            } else {
                // 关闭资源
                this.STOP_WATCH.start("关闭资源");
                this.close(this.DOCX_FILE_INPUT_STREAM);
                this.STOP_WATCH.stop("关闭资源");
            }
        }
        // 赋值执行计划
        this.prettyPrint = this.STOP_WATCH.prettyPrint(TimeUnit.MILLISECONDS);
        // 反转顺序
        return ListUtil.reverse(this.QUESTION_DATA_LIST);
    }

    protected void preCheck(InputStream docxFileInputStream) throws Exception {
        // 校验文件名称
        Assert.notBlank(this.FILE_NAME_AND_TYPE, "导入Word文件名称为空");
        List<String> fileNameAndTypeSplitList = Assert.notEmpty(StrUtil.split(this.FILE_NAME_AND_TYPE, StrPool.DOT),
                "导入Word文件名称【{}】不是可解析的格式，示例: 文件名称.docx", this.FILE_NAME_AND_TYPE);
        if (fileNameAndTypeSplitList.size() != 2) {
            throw new IllegalArgumentException(
                    StrUtil.format("导入Word文件名称【{}】不是可解析的格式，示例: 文件名称.docx", this.FILE_NAME_AND_TYPE));
        }
        // 文件名称
        String wordFileName = Assert.notBlank(fileNameAndTypeSplitList.get(0), "解析的Word文件名称为空").trim();
        // 校验文件名称
        Assert.checkBetween(wordFileName.length(), 1, 255, "文件名称的字符长度超过了限制，限制为1~255个字符");
        // 文件类型转换成小写
        String wordFileType = Assert.notBlank(fileNameAndTypeSplitList.get(1), "解析的Word文件类型为空").trim().toLowerCase();
        // 校验文件类型
        Assert.isTrue(WORD_FILE_TYPE_NAME_LIST.contains(wordFileType), "文件类型不匹配，当前支持Word类型: {}",
                StrUtil.join("|", WORD_FILE_TYPE_NAME_LIST));

        Assert.notNull(this.TEMPLATE_DEFINITION_CLASS, "模版定义类对象为空");
        Assert.notNull(this.DOCX_FILE_INPUT_STREAM, "word文件流为空");
        Assert.notNull(this.STOP_WATCH, "word文件流为空");
        Assert.notNull(this.STOP_WATCH, "word文件流为空");
        if (this.enableFileRemoteMapping) {
            Assert.notNull(this.wordPictureUploadInterface, "文件上传服务器接口未实现");
        } else {
            Assert.notBlank(this.localSaveFileBaseDir, "本地文件保存基础目录为空");
        }
        if (this.fixedPicWidth < 1) {
            if (this.minPicWidth < 1) {
                throw new RuntimeException("最小图片宽度小于1");
            }
            if (this.maxPicWidth < 1) {
                throw new RuntimeException("最大图片宽度小于1");
            }
        }
        if (this.fixedPicHeight < 1) {
            if (this.minPicHeight < 1) {
                throw new RuntimeException("最小图片高度小于1");
            }
            if (this.maxPicHeight < 1) {
                throw new RuntimeException("最大图片宽度小于1");
            }
        }
        if (this.enableConcurrent) {
            if (this.threadPoolExecutor == null) {
                log.warn("开启并发Word导入模式，但未配置线程池，不建议这样使用，可能会导致线程创建过多而产生内存错误问题。可通过threadPoolExecutor方法传入线程池实例对象");
                // 创建默认线程池，线程满了则抛出异常
                this.threadPoolExecutor = new ThreadPoolExecutor(
                        10,
                        50,
                        1,
                        TimeUnit.SECONDS,
                        new LinkedBlockingQueue<>(100),
                        // 满队列线程则抛出异常
                        new ThreadPoolExecutor.AbortPolicy());
                this.newThreadPoolStatus = true;
            }
        }
    }

    protected void loadPrepareData(InputStream docxFileInputStream) {
        // 自行实现
    }

    protected WordTemplateTree buildTemplateTree(Class<?> templateDefinitionClass) {
        this.STOP_WATCH.start("构建模版树对象");
        List<Tree<Integer>> treeList = new ArrayList<>(16);
        List<WordTemplateTreeNode> templateTreeNodeList = new ArrayList<>(16);
        // 构建完整树结构信息
        this.buildFullTreeInfo(templateDefinitionClass, treeList, null, new AtomicInteger(0));
        // 构建完整模板树结构数据
        AtomicInteger level = new AtomicInteger(0);
        this.buildFullTemplateTreeInfo(templateDefinitionClass, templateTreeNodeList, null, level, new AtomicInteger(0));
        WordTemplateTree wordTemplateTree = new WordTemplateTree(treeList, templateTreeNodeList, level.intValue());
        this.STOP_WATCH.stop("构建模版树对象");
        return wordTemplateTree;
    }

    protected CompletableFuture<WordTemplateTree> asyncBuildTemplateTree(Class<?> templateDefinitionClass) {
        return CompletableFuture.supplyAsync(() -> this.buildTemplateTree(templateDefinitionClass), this.threadPoolExecutor);
    }

    protected String loadWordText(Class<?> templateDefinitionClass) throws Exception {
        // 自行实现
        return "";
    }

    protected CompletableFuture<String> asyncLoadWordText(Class<?> templateDefinitionClass) {
        return CompletableFuture.supplyAsync(() -> "", this.threadPoolExecutor);
    }

    protected Map<String, String> loadInnerFileMapping() throws Exception {
        // 自行实现
        return new HashMap<>(0);
    }

    protected CompletableFuture<Map<String, String>> asyncLoadInnerFileMapping() {
        return CompletableFuture.supplyAsync(() -> {
            try {
                return this.loadInnerFileMapping();
            } catch (Exception e) {
                throw new BizException("加载图片、视频等文件映射数据发生异常", e);
            }
        }, threadPoolExecutor);
    }

    protected void preProcessor() {
        // 自行实现
    }

    protected void parseObj(List<WordImportQuestionData> questionDataList, WordTemplateTree wordTemplateTree) throws Throwable {
        // 自行实现
    }

    protected void postProcessor(List<WordImportQuestionData> questionDataList) throws Exception {
        // 自行实现
    }

    protected void close(InputStream docxFileInputStream) throws IOException {
        // 关闭文件输入流
        if (docxFileInputStream != null) {
            docxFileInputStream.close();
        }
        // 关闭线程池
        if (this.newThreadPoolStatus) {
            if (this.threadPoolExecutor != null) {
                this.threadPoolExecutor.shutdown();
            }
        }
    }

    /**
     * 输出执行计划
     *
     * @return 执行计划字符串
     */
    public String printExecutePlan() {
        return this.prettyPrint;
    }

    /**
     * 构建完整树结构信息
     *
     * @param templateDefinitionClass 模板定义类对象
     * @param treeNodeInfoList        模板树节点信息列表
     * @param treeNodeInfo            父级模板树节点
     * @param incr                    当前树节点数量
     */
    private void buildFullTreeInfo(Class<?> templateDefinitionClass, List<Tree<Integer>> treeNodeInfoList,
                                   Tree<Integer> treeNodeInfo, AtomicInteger incr) {
        List<Tree<Integer>> childrenList = new ArrayList<>(16);
        // 遍历所有属性信息
        for (Field field : ReflectUtil.getFields(templateDefinitionClass)) {
            // 获取映射节点名称注解
            MappingNode mappingNodeAnno = field.getAnnotation(MappingNode.class);
            if (mappingNodeAnno == null) {
                continue;
            }
            incr.incrementAndGet();
            Tree<Integer> treeNodeSave = new Tree<>();
            treeNodeSave.setId(incr.intValue());
            String name = mappingNodeAnno.nodeName();
            treeNodeSave.setName(name);
            childrenList.add(treeNodeSave);
            // 父级模板树节点对象为空则说明处在树的第一层
            if (treeNodeInfo == null) {
                treeNodeSave.setParentId(0);
                treeNodeInfoList.add(treeNodeSave);
            } else {
                treeNodeSave.setParentId(treeNodeInfo.getId());
            }
            // 触底
            if (StrUtil.contains(field.getType().getPackage().getName(), "java")) {
                continue;
            }
            // 递归
            this.buildFullTreeInfo(field.getType(), treeNodeInfoList, treeNodeSave, incr);
        }
        // 赋值子节点列表数据
        if (treeNodeInfo != null) {
            treeNodeInfo.setChildren(childrenList);
        }
    }

    /**
     * 树节点是否有下一个层级
     */
    private boolean nextLevel = true;

    /**
     * 构建完整模板树结构信息
     *
     * @param templateDefinitionClass 模板定义类对象
     * @param templateTreeNodeList    模板树节点信息列表
     * @param templateTreeNode        父级模板树节点
     * @param level                   当前树的层数
     * @param incr                    当前树节点数量
     */
    private void buildFullTemplateTreeInfo(Class<?> templateDefinitionClass, List<WordTemplateTreeNode> templateTreeNodeList,
                                           WordTemplateTreeNode templateTreeNode, AtomicInteger level, AtomicInteger incr) {
        List<WordTemplateTreeNode> childrenList = new ArrayList<>(16);
        // 遍历所有属性信息
        for (Field field : ReflectUtil.getFields(templateDefinitionClass)) {
            // 获取映射节点名称注解
            MappingNode mappingNodeAnno = field.getAnnotation(MappingNode.class);
            if (mappingNodeAnno == null) {
                continue;
            }
            incr.incrementAndGet();
            if (nextLevel) {
                level.incrementAndGet();
            }
            WordTemplateTreeNode templateTreeNodeSave = new WordTemplateTreeNode();
            templateTreeNodeSave.setId(incr.intValue());
            String name = mappingNodeAnno.nodeName();
            templateTreeNodeSave.setNodeName(name);
            // 赋值试题分隔符号
            String questionSplitter = mappingNodeAnno.questionSplitter();
            if (StrUtil.isNotBlank(questionSplitter)) {
                templateTreeNodeSave.setQuestionSplitterStatus(true);
                templateTreeNodeSave.setQuestionSplitterStr(questionSplitter);
            }
            // 赋值试题题目分隔符号
            String questionTitleSplitter = mappingNodeAnno.questionTitleSplitter();
            if (StrUtil.isNotBlank(questionTitleSplitter)) {
                templateTreeNodeSave.setQuestionTitleSplitterStatus(true);
                templateTreeNodeSave.setQuestionTitleSplitterStr(questionTitleSplitter);
            }
            // 赋值必传
            if (mappingNodeAnno.must()) {
                templateTreeNodeSave.setMustStatus(true);
            }
            childrenList.add(templateTreeNodeSave);
            // 父级模板树节点对象为空则说明处在树的第一层
            if (templateTreeNode == null) {
                templateTreeNodeSave.setParentId(0);
                templateTreeNodeList.add(templateTreeNodeSave);
            } else {
                templateTreeNodeSave.setParentId(templateTreeNode.getId());
                templateTreeNodeSave.setParentNodeName(templateTreeNode.getNodeName());
            }
            // 触底
            if (StrUtil.contains(field.getType().getPackage().getName(), "java")) {
                templateTreeNodeSave.setLastLevel(true);
                nextLevel = false;
                continue;
            }
            // 递归
            this.buildFullTemplateTreeInfo(field.getType(), templateTreeNodeList, templateTreeNodeSave, level, incr);
        }
        // 赋值子节点列表数据
        if (templateTreeNode != null) {
            templateTreeNode.setChildrenList(childrenList);
        }
    }

    public void asyncClose() {
        this.asyncClose = true;
    }

    public void setMinPicWidth(int minPicWidth) {
        this.minPicWidth = minPicWidth;
    }

    public void setMinPicHeight(int minPicHeight) {
        this.minPicHeight = minPicHeight;
    }

    public void enableFileRemoteMapping() {
        this.enableFileRemoteMapping = true;
    }

    public void nonThrowException() {
        this.throwExceptionStatus = false;
    }

    public void setMaxPicWidth(int maxPicWidth) {
        this.maxPicWidth = maxPicWidth;
    }

    public void setMaxPicHeight(int maxPicHeight) {
        this.maxPicHeight = maxPicHeight;
    }

    public void setFixedPicWidth(int fixedPicWidth) {
        this.fixedPicWidth = fixedPicWidth;
    }

    public void setFixedPicHeight(int fixedPicHeight) {
        this.fixedPicHeight = fixedPicHeight;
    }

    public void setLocalSaveFileBaseDir(String localSaveFileBaseDir) {
        this.localSaveFileBaseDir = localSaveFileBaseDir;
    }

    public void setWordPicUploadInterface(WordPictureUploadInterface wordPictureUploadInterface) {
        this.wordPictureUploadInterface = wordPictureUploadInterface;
    }

    public void enableConcurrent() {
        this.enableConcurrent = true;
    }

    public void setThreadPoolExecutor(ThreadPoolExecutor threadPoolExecutor) {
        this.threadPoolExecutor = threadPoolExecutor;
    }

    public boolean getExceptionStatus() {
        return this.exceptionStatus;
    }

    public String getExceptionMsg() {
        return this.exceptionMsg;
    }

    public Exception getException() {
        return this.exception;
    }
}
